package com.ccrfid.rmc6.service.toolcase;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.toolcase.ToolCaseEntity;
import com.ccrfid.rmc6.entity.toolcase.ToolEntity;
import com.ccrfid.rmc6.entity.toolcase.ToolTakeRecordEntity;
import com.ccrfid.rmc6.entity.toolcase.ToolTakeRecordView;
import com.ccrfid.rmc6.pojo.dto.LocatorAssetCount;
import com.ccrfid.rmc6.repository.search.SearchCriteria;
import com.ccrfid.rmc6.repository.toolcase.ToolCaseRepository;
import com.ccrfid.rmc6.repository.toolcase.ToolRepository;
import com.ccrfid.rmc6.repository.toolcase.ToolTakeRecordRepository;
import com.ccrfid.rmc6.repository.toolcase.ToolTakeRecordViewRepository;
import com.ccrfid.rmc6.util.GeomUtil;
import org.hibernate.query.NativeQuery;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.dialect.mysql.MySQLGeometryTypeDescriptor;
import org.hibernate.transform.Transformers;
import org.hibernate.type.IntegerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

@Service
@Transactional
public class ToolCaseServiceImpl implements ToolCaseService {
    private static final Logger logger = LoggerFactory.getLogger(ToolCaseServiceImpl.class);

    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private ToolCaseRepository toolCaseRepository;
    @Autowired
    private ToolRepository toolRepository;
    @Autowired
    private ToolTakeRecordRepository toolTakeRecordRepository;
    @Autowired
    private ToolTakeRecordViewRepository toolTakeRecordViewRepository;

    @Override
    public Page<ToolCaseEntity> getToolCasePage(SearchCriteria criteria, Pageable pageable) {
        return toolCaseRepository.search(criteria, pageable);
    }

    private void assertToolCaseIdNotExists(Long toolCaseId) throws AppException {
        if (toolCaseId == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.param_is_null.getCode(), CustomErrors.param_is_null.getReason(), "");
        }
        if (!toolCaseRepository.existsById(toolCaseId)) {
            throw new AppException(Response.Status.NOT_FOUND.getStatusCode(),
                    CustomErrors.tool_case_not_found.getCode(), CustomErrors.tool_case_not_found.getReason(), "");
        }
    }

    @Override
    public ToolCaseEntity getToolCaseById(Long toolCaseId) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);

        return toolCaseRepository.getById(toolCaseId);
    }

    @Override
    public ToolCaseEntity getToolCaseByCardNumber(Long cardNumber) throws AppException {
        return toolCaseRepository.getByCardNumber(cardNumber);
    }

    @Override
    public ToolCaseEntity createToolCase(ToolCaseEntity entity) throws AppException {
        entity.setTakeOut(0);
        return toolCaseRepository.save(entity);
    }

    @Override
    public ToolCaseEntity updateToolCase(Long toolCaseId, ToolCaseEntity entity) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);

        if (toolCaseId != entity.getId()) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.different_args.getCode(), CustomErrors.different_args.getReason(), "");
        }
        return toolCaseRepository.save(entity);
    }

    @Override
    public Boolean deleteToolCase(Long toolCaseId) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);
        //删除相关工具
        List<ToolEntity> toolList = toolRepository.getAllByToolCaseId(toolCaseId);
        if (toolList != null && toolList.size() > 0) {
            toolRepository.deleteAllByToolCaseId(toolCaseId);
        }
        //删除工具包
        if (toolCaseRepository.existsById(toolCaseId)) {
            toolCaseRepository.deleteById(toolCaseId);
        }
        return true;
    }

    @Override
    public List<ToolEntity> getToolsInToolCase(Long toolCaseId) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);

        return toolRepository.getAllByToolCaseId(toolCaseId);
    }

    @Override
    public ToolEntity createTool(Long toolCaseId, ToolEntity toolEntity) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);

        toolEntity.setToolCaseId(toolCaseId);
        return toolRepository.save(toolEntity);
    }

    private void assertToolIdNotExists(Long toolId) throws AppException {
        if (toolId == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.param_is_null.getCode(), CustomErrors.param_is_null.getReason(), "");
        }
        if (!toolRepository.existsById(toolId)) {
            throw new AppException(Response.Status.NOT_FOUND.getStatusCode(),
                    CustomErrors.tool_not_found.getCode(), CustomErrors.tool_not_found.getReason(), "");
        }
    }

    @Override
    public ToolEntity updateTool(Long toolCaseId, Long toolId, ToolEntity toolEntity) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);
        assertToolIdNotExists(toolId);

        toolEntity.setId(toolId);
        toolEntity.setToolCaseId(toolCaseId);
        return toolRepository.save(toolEntity);
    }

    @Override
    public Boolean deleteTool(Long toolCaseId, Long toolId) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);
        assertToolIdNotExists(toolId);

        toolRepository.deleteById(toolId);

        return true;
    }

    @Override
    public Boolean toolCaseTakeOut(Long toolCaseId, ToolTakeRecordEntity entity) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);

        if (!toolTakeRecordRepository.existsByToolCaseIdAndTakeBackTimeIsNull(toolCaseId)) {
            entity.setId(null);
            entity.setToolCaseId(toolCaseId);
            entity.setTakeOutTime(new Timestamp(new Date().getTime()));
            toolTakeRecordRepository.save(entity);
        } else {
            ToolTakeRecordEntity recordEntity = toolTakeRecordRepository.getByToolCaseIdAndTakeBackTimeIsNull(toolCaseId);
            recordEntity.setTakeOutTime(new Timestamp(new Date().getTime()));
            recordEntity.setBorrowerId(entity.getBorrowerId());
            recordEntity.setBorrowerName(entity.getBorrowerName());
            toolTakeRecordRepository.save(recordEntity);
        }

        ToolCaseEntity toolCaseEntity = toolCaseRepository.getById(toolCaseId);
        toolCaseEntity.setTakeOut(1);   //设置借出状态
        toolCaseRepository.save(toolCaseEntity);
        return true;
    }

    @Override
    public Boolean toolCaseTakeBack(Long toolCaseId) throws AppException {
        assertToolCaseIdNotExists(toolCaseId);
        if (toolTakeRecordRepository.existsByToolCaseIdAndTakeBackTimeIsNull(toolCaseId)) {
            ToolTakeRecordEntity recordEntity = toolTakeRecordRepository.getByToolCaseIdAndTakeBackTimeIsNull(toolCaseId);
            recordEntity.setTakeBackTime(new Timestamp(new Date().getTime()));
            toolTakeRecordRepository.save(recordEntity);
        }
        ToolCaseEntity toolCaseEntity = toolCaseRepository.getById(toolCaseId);
        toolCaseEntity.setTakeOut(0);   //设置借出状态
        toolCaseRepository.save(toolCaseEntity);
        return true;
    }

    @Override
    public Page<ToolTakeRecordView> getToolCaseTakeRecords(SearchCriteria criteria, Pageable pageable) {
        return toolTakeRecordViewRepository.search(criteria, pageable);
    }

    @Override
    public List<LocatorAssetCount> getToolCaseDistribution(Integer floorNumber) {
        String sql =
                "select a.locator_address as locatorAddress, a.pos, a.floor_number as floorNumber, count(*) as count " +
                " from tool_case c " +
                " left join v_card_active a on c.card_number = a.card_number " +
                " where c.is_enabled = 1 and a.floor_number = :fn " +
                " group by a.pos, a.locator_address";
        List<LocatorAssetCount> list = entityManager.createNativeQuery(sql)
                .setParameter("fn", floorNumber)
                .unwrap(NativeQuery.class)
                .addScalar("locatorAddress", IntegerType.INSTANCE)
                .addScalar("pos", new JTSGeometryType(MySQLGeometryTypeDescriptor.INSTANCE))
                .addScalar("floorNumber", IntegerType.INSTANCE)
                .addScalar("count", IntegerType.INSTANCE)
                .setResultTransformer(Transformers.aliasToBean(LocatorAssetCount.class))
                .list();
        return list;
    }

    @Override
    public List<ToolCaseEntity> getToolCaseUnderLocator(Integer locatorNumber) {
        String sql = "select c.* from tool_case c " +
                " left join d_card_active a on c.card_number = a.card_number " +
                " where c.is_enabled = 1 and a.locator_address = :locatorNumber";
        List<ToolCaseEntity> list = entityManager.createNativeQuery(sql, ToolCaseEntity.class)
                .setParameter("locatorNumber", locatorNumber)
                .getResultList();
        return list;
    }

    @Override
    public List<ToolCaseEntity> getToolCaseUnderPos(double x, double y) {
        String sql = "select c.* from tool_case c " +
                " left join d_card_active a on c.card_number = a.card_number " +
                " where c.is_enabled = 1 and st_contains(:pg, point(a.x, a.y))";
        List<ToolCaseEntity> list = entityManager.createNativeQuery(sql, ToolCaseEntity.class)
                .setParameter("pg", GeomUtil.INSTANCE.createSmallPolygon(x, y))
                .getResultList();
        return list;
    }
}
